package com.zrc.japktool.apk.zipalignsign;

import com.android.apksig.ApkSigner;
import com.android.apksig.ApkSigner.SignerConfig;
import com.android.apksig.ApkSigner.SignerConfig.Builder;
import com.zrc.japktool.util.FileUtil;
import com.zrc.japktool.util.TaskLog;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStore.PasswordProtection;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStore.ProtectionParameter;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.stream.Collectors;

public class ApkSign {

    private TaskLog log = null;

    public ApkSign(TaskLog log) {
        this.log = log;
    }

    private PrivateKeyEntry loadKey(byte[] ksData, String ksPass, String keyAlias, String keyPass) throws IOException {
        PrivateKeyEntry privateKeyEntry = null;
        Set<String> passwordList = new TreeSet();
        if (ksPass != null) {
            passwordList.add(ksPass);
        }

        if (keyPass != null) {
            passwordList.add(keyPass);
        }

        passwordList.add("android");
        KeyStore keyStore = loadKeyStore(ksData, passwordList);
        ArrayList aliasesList = new ArrayList();

        String alias;
        try {
            Enumeration aliases = keyStore.aliases();

            while (aliases.hasMoreElements()) {
                alias = (String) aliases.nextElement();
                if (keyStore.isKeyEntry(alias)) {
                    aliasesList.add(alias);
                }
            }

            if (keyAlias != null) {
                aliasesList.remove(keyAlias);
                aliasesList.add(0, keyAlias);
            }
        } catch (KeyStoreException var16) {
            throw new RuntimeException(var16);
        }

        Iterator var17 = aliasesList.iterator();

        String pass;
        while (var17.hasNext()) {
            alias = (String) var17.next();

            for (String s : passwordList) {
                pass = s;
                if (pass != null) {
                    try {
                        ProtectionParameter param = new PasswordProtection(pass.toCharArray());
                        privateKeyEntry = (PrivateKeyEntry) keyStore.getEntry(alias, param);
                        break;
                    } catch (Exception var15) {
                    }
                }
            }

            if (privateKeyEntry != null) {
                break;
            }
        }

        if (privateKeyEntry == null) {
            throw new IOException("apk签名: fail load key from keystore");
        } else {
            X509Certificate certificate = (X509Certificate) privateKeyEntry.getCertificate();
            log.info("apk签名: 已加载签名证书 " + certificate.getSubjectDN());
            try {
                byte[] encoded = certificate.getEncoded();
                MessageDigest md5 = MessageDigest.getInstance("MD5");
                pass = toHexString(md5.digest(encoded));
                log.info("apk签名: 签名证书MD5: " + pass);
                MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
                String sha256hex = toHexString(sha256.digest(encoded));
                log.info("apk签名: 签名证书SHA256: " + sha256hex);
            } catch (Exception var14) {
            }

            return privateKeyEntry;
        }
    }

    private String toHexString(byte[] digest) {
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) {
            sb.append(String.format("%02x", b & 255));
        }
        return sb.toString();
    }

    private KeyStore loadKeyStore(byte[] ksData, Set<String> passwordList) throws IOException {
        List<String> storeTypes = Arrays.asList("PKCS12", "JKS", KeyStore.getDefaultType());
        Set<String> passwordSet = new HashSet();
        if (passwordList != null) {
            passwordSet.addAll(passwordList);
        }
        passwordSet.add("android");
        Iterator var4 = storeTypes.iterator();

        label34:
        while (var4.hasNext()) {
            String type = (String) var4.next();
            Iterator var6 = passwordSet.iterator();

            while (true) {
                String password;
                do {
                    if (!var6.hasNext()) {
                        continue label34;
                    }

                    password = (String) var6.next();
                } while (password == null);

                try {
                    KeyStore keyStore = KeyStore.getInstance(type);
                    keyStore.load(new ByteArrayInputStream(ksData), password.toCharArray());
                    log.info("apk签名: 已加载类型为 " + type + " 的密钥库");
                    return keyStore;
                } catch (Exception var9) {
                }
            }
        }
        log.error("apk签名: 无法加载" + storeTypes + "类型、错误类型或错误密码的密钥库");
        throw new IOException("apk签名: 读取签名文件失败...");
    }

    private void signTask(byte[] ksData, String keystorePass, String keyAlias, String keyPass, boolean isAAB, Path inApkPath, Path outApkPath) throws Exception {

        File apkFile = inApkPath.toFile();
        File outDirFile = outApkPath.toFile();

        PrivateKeyEntry key = loadKey(ksData, keystorePass, keyAlias, keyPass);
        List<X509Certificate> certificates = Arrays.stream(key.getCertificateChain())
                .map(c -> (X509Certificate) c)
                .collect(Collectors.toList());
        SignerConfig signerConfig = new Builder("cert", key.getPrivateKey(), certificates).build();
        ApkSigner signer = new ApkSigner.Builder(Collections.singletonList(signerConfig))
                .setOtherSignersSignaturesPreserved(false)
                .setV3SigningEnabled(false)
                .setInputApk(apkFile)
                .setOutputApk(outDirFile)
                .setV1SigningEnabled(true)
                .setV2SigningEnabled(true)
                .setMinSdkVersion(isAAB ? 26 : 0)
                .build();
        signer.sign();
    }

    public void sign1(String keystorePass, String keyAlias, String keyPass, boolean isAAB, Path inApkPath, Path outApkPath) throws Exception {
        log.tip("apk签名: " + "开始");
        log.info("apk签名: jiuxiao.jks: 内置签名文件");
        log.info("apk签名: keystorePass: " + keystorePass);
        log.info("apk签名: keyPass: " + keyPass);
        log.info("apk签名: isAAB: " + isAAB);
        log.info("apk签名: inApkPath: " + inApkPath);

        signTask(FileUtil.getResourceAsBytes("common/jiuxiao.jks"), keystorePass, keyAlias, keyPass, isAAB, inApkPath, outApkPath);
        log.info("apk签名: outApkPath: " + outApkPath);
        log.tip("apk签名: 结束");
    }

    public void sign(String keystorePass, String keyAlias, String keyPass, boolean isAAB, Path inApkPath, Path outApkPath) throws Exception {
        log.tip("apk签名: " + "开始");
        log.info("apk签名: debug.keystore: 内置签名文件");
        log.info("apk签名: keystorePass: " + keystorePass);
        log.info("apk签名: keyPass: " + keyPass);
        log.info("apk签名: isAAB: " + isAAB);
        log.info("apk签名: inApkPath: " + inApkPath);
        FileUtil.getResourceAsBytes("common/debug.keystore");
        signTask(FileUtil.getResourceAsBytes("common/debug.keystore"), keystorePass, keyAlias, keyPass, isAAB, inApkPath, outApkPath);
        log.info("apk签名: outApkPath: " + outApkPath);
        log.tip("apk签名: 结束");
    }

    public void sign(Path keystorePath, String keystorePass, String keyAlias, String keyPass, boolean isAAB, Path inApkPath, Path outApkPath) throws Exception {
        log.tip("apk签名: 开始");
        log.info("apk签名: >keystorePath: " + keystorePath);
        log.info("apk签名: keystorePass: " + keystorePass);
        log.info("apk签名: keyPass: " + keyPass);
        log.info("apk签名: isAAB: " + isAAB);
        log.info("apk签名: inApkPath: " + inApkPath);
        signTask(Files.readAllBytes((keystorePath)), keystorePass, keyAlias, keyPass, isAAB, inApkPath, outApkPath);
        log.info("apk签名: outApkPath: " + outApkPath);
        log.tip("apk签名: 结束");
    }

    public static void main(String[] args) throws Exception {
        try {
            ApkSign apkSign = new ApkSign(TaskLog.newTaskLog());
            String keyStorePath = "C:/Users/15666/Desktop/test/mini222.jks";
            String inputAlias = "mini222";
            String inputKsPass = "111111111";
            String inputKeyPass = "111111111";
            apkSign.sign(Paths.get(keyStorePath),
                    inputKsPass,
                    inputAlias,
                    inputKeyPass,
                    false,
                    Paths.get("C:/Users/15666/Desktop/test/demo-zipalign.apk"),
                    Paths.get("C:/Users/15666/Desktop/test/demo-zipalign-sign.apk")
            );
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}